/**********************************************************************************************************************
 *
 * Project:    mGameEngine
 * File:       cloth.h
 * Author:     karooolek
 * Created on: 2009-09-29
 *
 **********************************************************************************************************************/

#ifndef CLOTH_H_
#define CLOTH_H_

#include "actor.h"
#include "body.h"

namespace mGameEngine
{
namespace Physics
{

/**
 * Dynamic cloth.
 * ??? TODO
 */
class Cloth : public Actor
{
protected:
    Vector<Vector3> _vertices;

    Vector<Vector3> _normals;

    Vector<uint16> _indices;

    uint32 _vertNumber;
    
    uint32 _indNumber;

    List<Body *> _attachedBodies;

    /**
     * Density.
     */
    float _density;

    /**
     * Bending factor.
     */
    float _bending;

    /**
     * Stretching factor.
     */
    float _stretching;
    
    /**
     * PhysX cloth.
     */
    mutable NxCloth *_cloth;

    /**
     * PhysX actors for joints.
     */
    mutable List<NxActor *> _jointActors;

public:
    /**
     * Create PhysX actor for joint.
     * @param joint joint.
     * @return PhysX actor for joint.
     */
    virtual NxActor *_createJointActor(Joint *joint) const;

    // TOTO well thought method to update/synchronize extrenal buffers
    // virtual void _updateBuffers(

    const Vector<Vector3> &getVertices() const
    {
        return _vertices;
    }
    
    const Vector<Vector3> &getNormals() const
    {
        return _normals;
    }
    
    const Vector<uint16> &getIndices() const 
    {
        return _indices;
    }

    const List<Body *> &getAttachedBodies() const
    {
        return _attachedBodies;
    }

public:
    Cloth();

    Cloth(const Vector<Vector3> &vertices, const Vector<Vector3> &normals, const Vector<uint16> &indices,
          float density = 1.0f, float bending = 0.5f, float stretching = 0.5f);

    virtual ~Cloth();

    /**
     * Get cloth density.
     * @return cloth density.
     */
    float getDensity() const
    {
        return _density;
    }

    /**
     * Set cloth density.
     * @param density density.
     */
    virtual void setDensity(float density);

    /**
     * Get cloth bending factor.
     * @return cloth bending factor.
     */
    float getBending() const
    {
        return _bending;
    }

    /**
     * Set cloth bending factor.
     * @param bending bending factor.
     */
    virtual void setBending(float bending);

    /**
     * Get cloth stretching factor.
     * @return cloth stretching factor.
     */
    float getStretching() const
    {
        return _stretching;
    }

    /**
     * Set cloth stretching factor.
     * @param stretching stretching factor.
     */
    virtual void setStretching(float stretching);

    /**
     * Attach cloth to body.
     * @param body body to attach.
     */
    virtual void attachToBody(Body *body);

    /**
     * Put force/momentum to cloth.
     * @param force force vector.
     * @param radius radius at which force is put.
     */
    virtual void putForce(const Vector3 &force, const Vector3 &radius = Vector3());

    /**
     * Put force/momentum impulse to cloth.
     * @param impulse force vector.
     * @param radius radius at which force is put.
     */
    virtual void putImpulse(const Vector3 &impulse, const Vector3 &radius = Vector3());

    /**
     * Put acceleration to cloth.
     * @param acc acceleration vector.
     * @param radius radius at which acceleration is put.
     */
    virtual void putAcceleration(const Vector3 &acc, const Vector3 &radius);

    /**
     * Put acceleration to cloth to each vertex.
     * @param acc acceleration vector.
     */
    virtual void putAcceleration(const Vector3 &acc);

    /**
     * Create cloth on PhysX scene.
     * @param scene PhysX scene.
     */
    virtual void create(NxScene *scene) const;

    /**
     * Get cloth transformation.
     * @return always identity matrix.
     */
    virtual Matrix4x4 getTransformation() const
    {
        return Matrix4x4(1.0f);
    }

    /**
     * Do nothing.
     * Transformation matrices don't apply to cloths.
     * @param t transformation.
     */
    virtual void setTransformation(const Matrix4x4 &t)
    {
    }

    /**
     * Get cloth point linear velocity.
     * @param p cloth point.
     */
    virtual Vector3 getPointVelocity(const Vector3 &p);

    virtual void updateExternalVertices(Vector3 *positions, uint8 positionsStride,
                                        Vector3 *normals, uint8 normalsStride) const;
};

}
}

#endif // CLOTH_H_
